/* All definitions for engine appear here */
#ifndef __ENGINE_H__
#define __ENGINE_H__
#include <fstream>
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include "tinyxml.h"
#include "xpath_processor.h"
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include "SDL_net.h"

using std::vector;
using std::map;
using std::string;
using std::cout;
using std::cerr;

#ifdef DEBUG
	#define E(_m_) cerr << "[ERR] " << _m_ << endl;
#else
	#define E(_m_) ;
#endif

/* external functions */
#include "luaxml/luaxml.h"
#include "luagl/luagl.h"

#ifdef __WIN32__
	#define OSSEP "\\"
#else
	#define OSSEP "/"
#endif



/* Following is a guideline */
typedef void (*simple_function_pointer)(); 

class AbstractLanguageEnvironment { /**< An abstract definition of operations required from a language */
	public:
		string lang_name; /**< Name of the language */
		string lang_ver;  /**< Version of language */
		AbstractLanguageEnvironment();
		~AbstractLanguageEnvironment();
		int call(string function_name); /**< Basic method to call a function \return 1 on success */
		void create_link(simple_function_pointer ptr,string language_call_name); /**< A way of linking a C/C++ function to a script call */
		void remove_link(string language_call_name); /**< A way of unlinking a C/C++ function from a script call */
		void load_library_1(); /**< A way of loading libraries */
};


/* End guideline */

extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

typedef int (*LUA_APIFN)(lua_State*);
struct ltstr_stl { bool operator() ( string  s1, string  s2)    const { return s1.compare(s2)< 0;  } };



class LuaEnvironment {
	public:
		string lang_name,lang_ver;
		lua_State *L;
		int isLoadedXml,isLoadedGL,isLoadedNet;
		LuaEnvironment();
		LuaEnvironment(int restricted);
		~LuaEnvironment();
		inline void call(string function_name) {
			lua_getglobal(L,function_name.c_str());
			if(lua_pcall(L,0,0,0)) E("LuaEnvironment::call :" << "error while calling" << function_name << "\n" << lua_tostring(L,-1) << endl)
		}
		inline void call(string function_name,string arg) {
			lua_getglobal(L,function_name.c_str());
			lua_pushstring(L,arg.c_str());
			if(lua_pcall(L,1,0,0)) E("LuaEnvironment::call :" << "error while calling" << function_name << "\n" << lua_tostring(L,-1) << endl)
		}
		inline void call(string function_name,double arg) {
			lua_getglobal(L,function_name.c_str());
			lua_pushnumber(L,arg);
			if(lua_pcall(L,1,0,0)) E("LuaEnvironment::call :" << "error while calling" << function_name << "\n" << lua_tostring(L,-1) << endl)
		}
		inline void create_link(LUA_APIFN fnptr, string luafnname) {
			lua_pushstring(L,luafnname.c_str());
			lua_pushcfunction(L, fnptr);
			lua_settable(L,LUA_GLOBALSINDEX);
		}
		inline void remove_link(string luafnname) {
			lua_pushnil(L);
			lua_setglobal(L,luafnname.c_str());
		}

		inline void remove_link(char *luafname) {
			lua_pushnil(L);
			lua_setglobal(L,luafname);
		}
		inline void load_library(string name) { if(name == "xml") load_library_xml(); else if(name == "opengl") load_library_opengl(); }
		inline void load_library_xml() { if(!isLoadedXml) { isLoadedXml=1; luaopen_xml_ltxml(L); } }
		inline void load_library_opengl() { if(!isLoadedGL) { isLoadedGL=1; luaopen_opengl(L); } }
		inline void load_library_net();
		void load_restrictions(const char *filepath); /**< Loads restrictions from file. All banned operations appear on separate lines */
		inline int load_file(const char *filepath);
};


#define ARG3_1 -3
#define ARG3_2 -2
#define ARG3_3 -1

#define ARG2_1  -2
#define ARG2_2  -1

#define ARG1_1  -1

/* cannot use union here for sData and fData */
struct generic_data { unsigned char isNumber; struct { string sData; double fData; } store; };

struct generic_object {
	string name;
	map< double, generic_data> numStore;
	map< string, generic_data, ltstr_stl> stringStore;
	vector< generic_object* > nest;
}; /**< Generic Storage Object */
/**< basic maps required by all objects
 * id, type (string tag), x, y, w, h, _isvisible
 * and following event check functions: isOver(cursorx,cursory), isHit(cursorx,cursory) 
 */
typedef map<string, generic_data, ltstr_stl>::iterator   GENOBJ_SDATAITER;
typedef map<string, generic_object, ltstr_stl>::iterator DICTITER; 

class Dictionary { /**< Storage management class for generic_object */
	public:
		int err; /**< Indicates errors when they occur during get/set operations. On error value is less than zero */
		map< string, generic_object ,ltstr_stl> repository;
		Dictionary();  /**< Initialize repository */
		~Dictionary(); /**< Cleanup all allocations */	
		inline struct generic_object& operator[](string name) { 
			map< string, generic_object, ltstr_stl>::iterator tempiter = repository.find(name); 
			if(tempiter == repository.end()) 
				return (tempiter->second); 
			//else return NULL; 
			throw 1;
		} /* shelved b'cos what to return when not found */
		void add_duplicate(generic_object * src); 
		int LUA_get(lua_State* L); /**< \param object-name \param key-name(number/string) \return <i>Success: </i>value(string/number) <i>Error: </i> No value is returned */
		int LUA_set(lua_State* L); /**< \param object-name \param key-name(number/string) \param new-value(number/string) \return <i>Success: </i>1 <i>Error: </i> No value is returned */
		inline int LUA_new(lua_State *L) { /**< \param object-name \return <i>Success: </i> 1 <i>Error: </i> nothing </i> */
			if(lua_isstring(L,ARG1_1)) {
				string name = lua_tostring(L,ARG1_1);
				struct generic_object t;	t.name = name;

				repository[name] = t;

				lua_pushnumber(L,1); return 1; 

			}
			return 0;
		}
		
		
};
enum EVENTTYPE { 
		TYPE_KEYBOARD	=  1	,
		TYPE_MOUSEBUTTON 	=  2	,
		TYPE_MOUSEMOVE   	=  4	,
		TYPE_WMQUIT          =  8	,
		TYPE_WMRESIZE	=  16	,
		TYPE_WMEXPOSE        =  32
};
enum BUTTONSTATE { KEYPRESSED = 0x10000, KEYRELEASED = 0x20000 , MOUSEPRESSED = 0x40000 ,MOUSERELEASED = 0x80000 };

/* event num [ -buttonstate-           EVENT    ]
 *            <-- 16 bits --> | <-- 16 bits -->
 *            buttonstate = [ 00 00 mouse keybd ]
 */
// !!!! struct event_repo_object { vector<string> objects; vector<string> fncallname; };
class Eventkeeper {
	public:
		map< unsigned long, map<string, string, ltstr_stl>* > repository; /**< fingerprint -> list of [ objectname , callfnname ] */

		LuaEnvironment script_land;
		Eventkeeper() {}
		~Eventkeeper(); 
		inline void set_event(unsigned long fingerprint, string objectname, string fnname) {
			map< unsigned long, map<string, string, ltstr_stl>* >::iterator tempiter;
			map<string,string,ltstr_stl> * ptr = NULL;	
			fingerprint &= 0xFF;
			tempiter = repository.find(fingerprint);
			
			if(tempiter == repository.end()) { 
				ptr = new map<string,string,ltstr_stl>();
				if(!ptr) return;
				repository[fingerprint] = ptr;
			}else {
				ptr = tempiter->second;
			}
			(*ptr)[objectname] = fnname; /* update/set the new mapping */
		}
		inline int LUA_set_event(lua_State *L) {
			if(!(lua_isnumber(L,ARG3_1) && lua_isstring(L,ARG3_2) && lua_isstring(L,ARG3_3))) return 0; /* arg validation fail */
			unsigned long fingerprint = ((unsigned long)lua_tonumber(L,ARG3_1)) & 0xFF;
			set_event(fingerprint, string(lua_tostring(L,ARG3_2)),string(lua_tostring(L,ARG3_3)));
			return 1;
		}
};

typedef map<string, string,ltstr_stl>::iterator MAPITER;

class BaseModule { /**< tag -> fnname mapping lua automation module to be used by parser,renderer,etc */
	protected:
		map<string,string,ltstr_stl> mapping_tag_fn;
	public:
		inline int LUA_set_mapping(lua_State *L); /**< set operation for mapping_tag_fn \param map_target \param map_fnname \return <i>Success</i> 1 <i>Error</i> no value */
		inline int LUA_get_mapping(lua_State *L); /**< get operation for mapping_tag_fn map_target*/
};




class Parser : public BaseModule {
	public:
		LuaEnvironment script_land;

		Parser();
		~Parser() {}
		void read(const char *filepath);
};

class Renderer : public BaseModule {
	public:
		LuaEnvironment script_land;

		Renderer();
		~Renderer() {}
		void render(int frame);
};

class EventChecker {
	public:
		EventChecker();
		~EventChecker() {} 
		void do_events();
};

class Controller : public BaseModule {
	public:
		Renderer render_instance;
		Parser   parser_instance;
		EventChecker eventchecker_instance;

		Controller() { /**< \param rootpath for all resource access */
			
		}
		~Controller() {}
		void init(string); /**< \param file-input */
		void do_iterate_once(int frame);

};


class Util {
	public:
		inline static TiXmlElement* get_element(TiXmlElement* xmlElem,char *sText);
	    static Dictionary library,canvas,config; /**< System Shared Data */
		static Eventkeeper events;
		static Controller controller;
		/* config["SYS"].stringStore["PATH"] where all system scripts are stored  */
		/*static void create_binding(char *filepath,BaseModule &mapStore); */
		inline static string create_filepath(string s1,string s2) /**< Handle OS specific concatenation */
		 { return s1 + OSSEP + s2; }
		inline static void set_syspath(char* rootpath);
		inline static int LUA_get_mouse_loc(lua_State *L);   /**< \return x,y */
		inline static int LUA_ismouse_pressed(lua_State *L); /**< \param button-number \return <i>Yes</i> 1 <i>No</i> 0 <i>Error</i> -1(invalid paramter) or -2(invalid button number)(max=5) </i>*/
		inline static int LUA_iskey_pressed(lua_State *L); /**< \param key-number \return <i>Yes</i>1 <i>No</i> 0 */
};



/* XLUA functions are functions that are directly bindable to lua environment are sometimes a wrapper for LUA_ type functions */

inline int XLUA_library_new(lua_State *L);
inline int XLUA_library_get(lua_State *L);
inline int XLUA_library_set(lua_State *L);
inline int XLUA_canvas_new(lua_State *L);
inline int XLUA_canvas_get(lua_State *L);
inline int XLUA_canvas_set(lua_State *L);
inline int XLUA_config_new(lua_State *L);
inline int XLUA_config_get(lua_State *L);
inline int XLUA_config_set(lua_State *L);

inline int XLUA_set_event(lua_State *L);
inline int XLUA_parser_set_mapping(lua_State *L);
inline int XLUA_parser_get_mapping(lua_State *L);
inline int XLUA_renderer_set_mapping(lua_State *L);
inline int XLUA_renderer_get_mapping(lua_State *L);
inline int XLUA_controller_set_mapping(lua_State *L);
inline int XLUA_controller_get_mapping(lua_State *L);

#endif
